package ar.uba.is2.ec.config;

import java.util.Properties;

import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;

import ar.uba.is2.common.Const;
import ar.uba.is2.common.mensaje.Componente;
import ar.uba.is2.common.mensaje.Receptor;
import ar.uba.is2.common.mensaje.canal.Canal;
import ar.uba.is2.common.mensaje.canal.impl.CanalImpl;
import ar.uba.is2.common.mensaje.filtro.impl.FiltroDesordenaMensaje;
import ar.uba.is2.common.mensaje.filtro.impl.FiltroPierdeMensajes;
import ar.uba.is2.common.mensaje.protocolo.Protocolo;
import ar.uba.is2.common.mensaje.protocolo.impl.SterenProtocol;
import ar.uba.is2.common.mensaje.puerto.impl.PuertoArchivoFactory;
import ar.uba.is2.ec.comm.ModuloComunicacion;
import ar.uba.is2.ec.comm.impl.ModuloComunicacionImpl;
import ar.uba.is2.ec.matem.ModeloMatematico;
import ar.uba.is2.ec.matem.dao.impl.MedicionDaoImpl;
import ar.uba.is2.ec.matem.motor.impl.MotorDeReglasImpl;
import ar.uba.is2.ec.receptor.EstacionCentralReceptor;
import ar.uba.is2.ec.trmanager.ReceptorHeartbit;
import ar.uba.is2.ec.trmanager.ReceptorPersistenciaMensaje;

public class Configuracion {
	private static final String LOG4J_FILENAME = "log4j-ec.properties";

	private static final Logger logger = Logger.getLogger(Configuracion.class);
	
	private static final String archivoConfiguracion = "ec.properties";

	public Configuracion() {
	}

	public void iniciar() {
		configurarLogging();
		logger.info("Inicializando Estación Central");
		
		configurarContexto();
		
		configurarModuloComunicacion();
		
		configurarBus();
		
		configurarModeloMatematico();
		logger.info("Configuracion terminada");
	}

	private void configurarLogging() {
		PropertyConfigurator.configure(ClassLoader.getSystemResource(LOG4J_FILENAME));
	}

	private void configurarModeloMatematico() {
		logger.info("Configurando Modelo Matemático");
		MedicionDaoImpl dao = new MedicionDaoImpl();
		MotorDeReglasImpl motor = new MotorDeReglasImpl();
		
		ModeloMatematico modeloMatematico = new ModeloMatematico(dao, motor);
		
		Contexto.instance.setModeloMatematico(modeloMatematico);
	}

	private void configurarBus() {
		
		// Receptor hearbit: registra las TR activas y caidas 
		ReceptorHeartbit receptorHeartbit = new ReceptorHeartbit();
		
		Contexto.instance.getModuloComunicacion().registrarReceptor(receptorHeartbit);
		
		// Receptor persistencia: persiste los mensajes recibidos
		MedicionDaoImpl dao = new MedicionDaoImpl();
		
		ReceptorPersistenciaMensaje receptorPersistencia = new ReceptorPersistenciaMensaje(dao);
		
		Contexto.instance.getModuloComunicacion().registrarReceptor(receptorPersistencia);
		
		// Receptor dummy: avisa de la llegada de mensajes
		Contexto.instance.getModuloComunicacion().registrarReceptor(EstacionCentralReceptor.instance);
		
	}

	private void configurarContexto() {
		try {
			logger.info("Levantando propiedades");
			Contexto contexto = new Contexto();
			
			Properties propiedades = new Properties();
			propiedades.load(ClassLoader.getSystemResourceAsStream(archivoConfiguracion));
			contexto.setPropiedades(propiedades);
			
			Contexto.instance = contexto;
			
		} catch (Exception e) {
			throw new RuntimeException("No se pudo terminar la configuracion", e);
		}
	}

	private void configurarModuloComunicacion() {
		logger.info("Creando modulo de comunicacion");
		
		ModuloComunicacionImpl moduloComunicacion = new ModuloComunicacionImpl();
		
		Protocolo protocolo = configurarProtocolo(moduloComunicacion);
		
		moduloComunicacion.setProtocolo(protocolo);
		
		Contexto.instance.setModuloComunicacion(moduloComunicacion);
	}

	private Protocolo configurarProtocolo(ModuloComunicacion moduloComunicacion) {
		logger.info("Creando protocolo");
		long retryTime = Long.parseLong(Contexto.instance.get("ec.protocolo.steren.msg.retry"));
		long timeout = Long.parseLong(Contexto.instance.get("ec.protocolo.steren.msg.timeout"));
		SterenProtocol protocol = new SterenProtocol(moduloComunicacion, retryTime, timeout);
		
		Canal canalDeComunicacion = crearCanal(protocol);
		
		protocol.setCanal(canalDeComunicacion);
		
		return protocol;
	}

	private Canal crearCanal(Receptor receptor) {
		logger.info("Creando canales de comunicacion");
		CanalImpl canal = new CanalImpl();
		canal.setPuertoFactory(new PuertoArchivoFactory());
		canal.suscribirEntrada(Const.COMPONENTE_ESTACION_CENTRAL, receptor);
		
		String string = Contexto.instance.get("terminales.remotas.nombres");
		String[] trIds = string.split(",");
		
		for (String trId : trIds) {
			canal.suscribirSalida(new Componente(trId));
		}
		logger.info(String.format("Terminales remotas configuradas: %s", string));
		
		return canal;
	}
}
